Strumenti di Verifica e Validazione di Codice Java
نویسندگان
چکیده
Sistemi informatici sempre più complessi sono ormai costantemente parte della vita di tutti i giorni di milioni di persone. Le applicazioni software che gestiscono questi sistemi informatici diventano man mano sempre più grosse e difficili da gestire e per questo l’incidenza del numero di bug al loro interno è diventata sempre più alta. La creazione di un software perfetto, ossia senza bug, è un obiettivo, che teoremi e anni di esperienza, hanno dimostrato impossibile. Tuttavia, con le conoscenze attuali, è possibile fare molto di più di quanto è stato fatto in passato per ricercare automaticamente bug all’interno del software. Java negli ultimi anni è stato al centro delle attenzioni dei programmatori per vari motivi tra cui spicca la portabilità, questo ha fatto s̀ı che molti si siano interessati all’opportunità di studiare metodi per la verifica e la validazione di programmi Java. Alcuni dei tool più recenti consento di effettuare verifiche senza impattare notevolmente sui tempi di sviluppo, permettendone quindi l’impiego per qualsiasi tipo di applicazione. Questo articolo presenta un insieme di tool che, sfruttando tecniche diverse, si propongono di verificare e validare applicazioni sviluppate in Java. 1. INTRODUZIONE La validazione e la verifica delle applicazioni software è una tematica che sta suscitando molti interessi non solo nell’ambito della ricerca ma anche in ambito industriale. I cicli di sviluppo e mantenimento di applicazioni software sempre più complessi, non possono essere, come l’esperienza ha più volte dimostrato, immuni ai bug e spesso questi bug possono avere anche effetti disastrosi. Per questo motivo in molti ambiti industriali la verifica e la validazione del software è diventato uno degli step fondamentali del ciclo di sviluppo del software, soprattutto nel caso di sistemi critici. Oggi sono disponibili una serie di tool con un grande potenziale per aiutare gli sviluppatori nella ricerca degli errori di programmazione, alcuni dei quali non sarebbero facilmente individuabili altrimenti. Alcuni di questi tool sono anche in grado di rilevare automaticamente sia deadlock che violazioni delle sezioni critiche, due tra le più critiche tipologie di errori che spesso causano gravi problemi ai software in esecuzione. I tool che si occupano di analisi del software alla ricerca di bug possono essere distinti in due categorie principali a seconda di come avviene la verifica. • Analisi dinamica: esegue il codice dell’applicazione e durante l’esecuzione effettua una serie di analisi per verificare la correttezza dell’output prodotto. È un metodo rapido, ma fortemente legato alla capacità delle test suite usate di coprire tutti gli aspetti critici del codice analizzato. • Analisi statica: verifica il software senza eseguirlo. Fa uso di diverse tecniche tra cui i bug pattern e i modelli per la rappresentazione del codice e delle sue proprietà. L’analisi statica può essere più o meno dispendiosa in termini di tempo e di costo computazionale a seconda delle tecniche usate. In base al tool utilizzato e al tipo di bug ricercati, il risultato può essere più o meno preciso. Comunque nessun tool è in grado di dare la certezza dell’assenza di bug nel codice. La ricerca di errori di programmazione soffre essenzialmente di due problematiche: • falsi positivi: alcuni dei problemi individuati in realtà non lo sono. Questo tipo di problema non è critico, ma, se in quantità eccessiva, porta all’inutilizzabilità del tool perché richiede un ulteriore tempo di analisi dell’output per distinguere i bug reali dai falsi allarmi. • falsi negativi: non tutti i bug presenti nel codice vengono individuati. Questo tipo di problema è critico perché non rileva potenziali problemi. Per ridurre le occorrenze di questo tipo di inconveniente piuttosto che effettuare analisi general purpose è possibile effettuare delle analisi di codice mirate alla verifica di specifiche proprietà e quindi più precise. In questo articolo noi ci concentriamo su alcuni tool che si occupano della validazione e della verifica di applicazioni Java [16]. I tool presi in considerazione dalla nostra analisi effettuano tutti un’analisi di tipo statico: FindBugs, PMD, Esc/Java2, Java Pathfinder e Bandera. In Figura 1 vengono riassunti alcuni dettagli sui tool, quali le interfacce ed i metodi di verifica utilizzati. Nome Input Interf. Tecnologia FindBugs Bytecode CL,Gui Syntax, Dataflow PMD Source CL,Gui Syntax Esc/Java2 Source + Annotation CL,Gui Theorem Proving Pathfinder Source CL Model Cecking Bandera Source CL,Gui Model Cecking Figure 1: Riepilogo dei Tool L’articolo è organizzato come segue: le Sezioni 2 e 3 mostrano i tool FindBugs e PMD; in Sezione 4 viene presentato il tool Esc/Java2; la Sezione 5, dopo un’introduzione alle tecniche di Model Checking, presenta il tool Java PathFinder; nella Sezione 6 viene introdotto il tool Bandera; e infine, la Sezione 7 presenta un confronto fra i tool e riporta le conclusioni a cui siamo giunti analizzando i tool. 2. FINDBUGS FindBugs [19] è un prodotto open source originalmente sviluppato da Bill Poth e correntemente mantenuto da Bill Pugh e David Hovemeyer. Codice sorgente Java e la documentazione possono essere scaricati gratuitamente dal sito http://findbugs.sourceforge.net. All’interno della famiglia degli strumenti di analisi di codice Java FindBugs è probabilmente uno dei tool più intuitivi, perché si avvicina molto al nostro modo di interpretare e leggere codice sorgente. Questo perché, fra le varie tecniche di analisi statica esistenti, FindBugs adotta un approccio basato sulla scoperta di cosiddetti bug pattern all’interno del codice da analizzare [15]. Tali pattern sono frammenti o strutture di codice sorgente o combinazioni di istruzioni che molto probabilmente rappresentano errori di programmazione, e la loro natura prettamente sintattica facilita la comprensione dei rispettivi warnings da parte dei programmatori. Prima di entrare meglio nei dettagli dell’analisi basata su bug pattern, però, è importante notare che FindBugs ispeziona possibili errori di programmazione e non rappresenta un tool di verifica dello stile di programmazione. Verificatori di stile esaminano il codice per determinare se questo rispetta o meno determinate regole stilistiche, come quelle adottate all’interno di un progetto software che coinvolga più programmatori diversi. Tali regole, infatti, servono soprattutto per mantenere uno stile di codifica coerente in tutto il progetto, cosa che, di conseguenza, aumenta la leggibilità e la manutenibilità del codice. In generale però, non mantenere uno stile accordato, non deve per forza risultare in un errore. 2.1 Pattern e Detector built-in Per il riconoscimenti dei bug pattern sintattici, FindBugs fornisce un’architettura modulare che permette di aggiungere opportuni detector, uno per ciascun pattern da riconoscere. Tramite questi detector si specificano le proprietà e la struttura dei pattern in una maniera esplicita e dichiarativa direttamente in codice Java. FindBugs non analizza direttamente il codice sorgente Java, ma la sua versione compilata file .class). Si tratta pertanto di uno strumento di analisi di bytecode, cioè del “linguaggio macchina” della Java Virtual Machine. Per questo motivo, i detector adottati da FindBugs, internamente, sfruttano la libreria BCEL [2], una libreria open source di analisi di bytecode Java. Più precisamente, FindBugs propone una struttura di detector basata sul cosiddetto Visitor design pattern di BCEL: ogni detector visita singolarmente ciascuna classe della libreria o applicazione da analizzare ed esegue la sua analisi in maniera atomica ed indipendente da eventuali altri detector operanti sullo stesso codice. FindBugs è corredato da un numero elevato di rilevatori di bug pattern ricorrenti (la versione 0.9.1 ne fornisce circa 130), altri possono essere progettati su misura e aggiunti a piacere alla configurazione standard del tool. Usando tecniche di analisi statica relativamente semplici, possono essere implementati tanti diversi rivelatori automatici, uno per ciascun pattern nuovo da riconoscere. Indipendentemente dal fatto che un particolare detector sia stato progettato per soddisfare problemi di programmazione su misura o che faccia parte dei detector generali forniti insieme ad una versione particolare di FindBugs, questi possono essere raggruppati in quattro classi di riconoscitori funzionalmente diversi. In particolare, le diverse strategie di implementazione dei detector pongono i seguenti approcci analitici nel centro della loro attenzione: • Struttura della classe e gerarchia di ereditarietà. Questi detector guardano solamente alla struttura delle classi analizzate e controllano se le gerarchie di ereditarietà e le implementazioni di interfacce siano coerenti, senza tenere conto di eventuale codice sorgente all’interno delle classi. • Scansione lineare del codice. Questi detector analizzano più in dettaglio ogni singola classe ed “entrano” nei rispettivi metodi. Si basano su una lettura lineare del bytecode dei metodi da analizzare, in base alla quale aggiornano (se necessario) una macchina a stati finiti interna, ogni qualvolta un detector richieda il mantenimento di un certo stato durante l’analisi. Questi detector non usano eventuali informazioni disponibili sul flusso di controllo vero dell’esecuzione e piuttosto si basano su euristiche che ne permettono un’approssimazione efficace. • flusso di controllo. I detector di questa categoria si basano su un grafo accurato del flusso di controllo per ciascun metodo da analizzare. Non usando più approssimazioni del flusso di esecuzione, l’applicazione di questi detector risulta più onerosa in termini di velocità. • Flusso di dati. I detector più complicati si basano su un’analisi approfondita del flusso di dati, oltre che sull’analisi del flusso di controllo. Queste tecniche di analisi statica permettono di coprire un gran numero di pattern di errori e determinano, insieme alla scelta di limitare l’analisi al bytecode, il potere riconoscitivo dei detector. Conseguentemente, FindBugs si presta soprattutto per la scoperta delle seguenti classi di problemi o errori: • Problemi di correttezza per thread singoli. • Problemi di correttezza nella sincronizzazione di più thread cooperanti. • Questioni di performance. • Problemi di sicurezza o vulnerabilità del codice. Generalmente, più è complesso il pattern da riconoscere, più è complesso il suo detector. I detector basati sull’analisi del flusso di dati sono i più complessi, mentre quelli per l’analisi strutturale delle classi sono i più semplici. Comunque, i detector più complessi solo in pochi casi superano le 1000 righe di codice Java, e quasi la metà dei detector totali occupa meno di 100 righe. Date queste premesse e per una migliore comprensione, di seguito discuteremo qualche detector tipico, già presente in FindBugs; per motivi di spazio non approfondiremo tutti i pattern definiti. Dropped Exception. Iniziamo con un pattern molto ricorrente e di facile comprensione. Il detector Dropped Exception si concentra sulla gestione delle eccezioni in Java, e quindi sui blocchi try-catch. In particolare, questo detector individua tutti quei costrutti try-catch con blocco catch vuoto, cioè tutti quelli che scartano eventuali eccezioni. Spesso durante la stesura del codice un programmatore è convito che certe eccezioni non possano mai accadere e quindi ignora la gestione corretta delle relative eccezioni. Ma, soprattutto in base a modifiche successive al codice che alterano anche le premesse stesse alla base di quel particolare frammento di codice, le eccezioni potrebbero s̀ı essere lanciate. Questo molto probabilmente avrebbe come conseguenze comportamenti di runtime anomali da parte dell’applicazione. Trovare problemi connessi ad eccezioni non propriamente gestite risulta essere molto oneroso in pratica e spesso richiede molto tempo. Null Pointer Dereference, Redundant Comparison to Null. Chiamare un metodo o accedere a variabili di istanza tramite riferimenti al puntatore nullo (null) porta inevitabilmente ad una NullPointerException durante l’esecuzione dell’applicazione. Solitamente un programmatore è molto attento a questo tipo di errore, perché la NullPointerException spesso – se non gestita in maniera opportuna – causa il crash dell’intero programma, ma quando il riferimento del puntatore è determinato per esempio dall’esecuzione precedente di un qualche metodo, il puntatore può anche risultare nullo. Per questo, il detector Null Pointer Dereference individua quelle istruzioni che de-referenziano un puntatore potenzialmente nullo. Alla base dell’implementazione di questo detector c’è un’analisi del flusso di dati, ristretto all’interno del metodo sotto analisi. Il pattern adottato non determina direttamente se parametri passati a o ritornati da un metodo siano nulli, ma sfrutta eventuali condizioni if presenti in righe precedenti al potenziale eccezione. Nel seguente frammento, per esempio, il puntatore foo è sicuramente nullo all’interno del blocco della condizione:
منابع مشابه
Testing Software Systems Against Realistic User Errors
Le persone organizzano sempre di più ogni aspetto della propria vita attraverso l’utilizzo di sistemi software, dal lavoro al trasporto alla salute. Possiamo essere sicuri che i programmi dai quali ci accingiamo a dipendere sono affidabili ? Tali programmi possono tollerare maltrattamenti degli utenti? Questa tesi affronta il problema di verificare i sistemi software rispetto ad errori introdot...
متن کاملODB-Tool: Validazione di Schemi e Ottimizzazione Semantica On-line per Basi di Dati Object oriented
In questo lavoro viene presentato ODB-Tool, uno strumento software per la validazione di schemi e l'ottimizzazione semantica di interrogazioni per le Basi di Dati Orientate agli Oggetti (OODB), che sfrutta le potenzialit a di Internet e del linguaggio JAVA per la rappresentazione gra ca on-line dei risultati ottenuti. Gli algoritmi operanti in ODB-Tool sono basati su tecniche di inferenza che s...
متن کاملAlcune riflessioni su code mobility
Nel corso degli ultimi anni vi è stato un tumultuoso sviluppo delle tecnologie legate all'uso di codice mobile. Tale sviluppo si concretizzato da un lato grazie all'avvento di Java e dei linguaggi che supportano la migrazione di un programma (secondo varie modalità) sui nodi di una rete di calcolatori e, dall'altro, dall'avvento di tecnologie di middleware sempre più sofisticate e capaci di int...
متن کاملCartella Clinica Elettronica su Piattaforma Java InfoBus
Descriviamo un sistema di cartella clinica elettronica che utilizza tecnologie internet di seconda generazione, tra le quali XML, XSL, CSS, JavaBeans e InfoBus, per distribuire in maniera sicura cartelle interattive di dati clinici nel web browser dei medici. Il sistema mette a disposizione strumenti grafici per la visualizzazione della cartella di un paziente, per la visione di immagini e per ...
متن کاملA New Dataset for Source Code Comment Coherence
English. Source code comments provide useful insights on a codebase and on the intent behind design decisions and goals. Often, the information provided in the comment of a method and in its corresponding implementation may be not coherent with each other (i.e., the comment does not properly describe the implementation). Several could be the motivations for this issue (e.g., comment and source ...
متن کامل